package work.linruchang.util.bean.wrapper.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.lang.Filter;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.ReUtil;
import cn.hutool.core.util.StrUtil;
import lombok.Data;
import lombok.experimental.Accessors;
import work.linruchang.util.EnhanceAnnotationUtil;
import work.linruchang.util.EnhanceAssert;
import work.linruchang.util.EnhanceBeanUtil;
import work.linruchang.util.EnhanceReflectUtil;
import work.linruchang.util.annotation.*;
import work.linruchang.util.bean.wrapper.db.SqlColumnInfo;
import work.linruchang.util.bean.wrapper.db.SqlFragment;
import work.linruchang.util.bean.wrapper.db.TableInfo;
import work.linruchang.util.bean.wrapper.QueryWrapper;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.*;

/**
 * 查询SQL构建器
 *
 * 使用请参考{cn.eshore.common.db.wrapper.QueryWrapperTest}
 * 配合数据库查询使用请查考{cn.eshore.test.SpringBootTest#test668()}
 *
 * {cn.eshore.service.common.dao.BaseServiceImpl} {cn.eshore.service.common.dao.impl.CommonDaoImpl}== 写好的一些CRUD接口
 *
 * {cn.eshore.service.ylj.service.impl.OnuMapperServiceImpl} {cn.eshore.common.facade.ylj.service.OnuMapperService} == 新的service如何继承使得BaseServiceImpl里面的接口可用
 * @author LinRuChang
 * @version 1.0
 * @date 2022/01/14
 * @since 1.8
 **/
@Data
@Accessors(chain = true)
public class QueryWrapperImpl<FirstTable> extends WhereWrapperImpl<FirstTable, QueryWrapper<FirstTable>> implements QueryWrapper<FirstTable> {

    private Set<SqlColumnInfo> selectColumnInfos = new LinkedHashSet<>();


    /**
     * 不同表但列名同需要处理 {selectColumnInfos}
     * 【由于JDBC查出来的结果会让非主表同名的字段值消失，所以需要取列别名进行处理此种情况】
     *
     * @return
     */
    private List<String> selectColumnNamesFormatter() {

        if (isCountFlag) {
            return new ArrayList<>(Arrays.asList(SqlFragment.SELECT_COUNT));
        }

        //去重
        final List<String> result = new ArrayList(selectColumnInfos.size());

        final List<String> columnNames = new ArrayList<>();
        CollUtil.forEach(selectColumnInfos.iterator(), new CollUtil.Consumer<SqlColumnInfo>() {
            @Override
            public void accept(SqlColumnInfo sqlColumnInfo, int index) {
                columnNames.add(StrUtil.blankToDefault(sqlColumnInfo.getColumnAlias(), sqlColumnInfo.getColumnName()));
            }
        });
        final Map<String, Integer> columnNameCounts = CollUtil.countMap(columnNames);

        //如果次数大于1且非主表字段则需取别名
        CollUtil.forEach(selectColumnInfos.iterator(), new CollUtil.Consumer<SqlColumnInfo>() {
            @Override
            public void accept(SqlColumnInfo sqlColumnInfo, int index) {
                TableInfo tableInfo = sqlColumnInfo.getTableInfo();
                String columnName = columnNames.get(index);
                if (columnNameCounts.get(columnName) > 1 && !tableInfo.getMasterTableFlag()) {
                    String columnAlias = sqlColumnInfo.getColumnAlias();
                    if (StrUtil.isNotBlank(columnAlias)) {
                        columnAlias = StrUtil.format("{}.{}", tableInfo.getTableAlias(), columnAlias);
                    } else {
                        columnAlias = StrUtil.format("{}.{}", tableInfo.getTableAlias(), sqlColumnInfo.getColumnName());
                    }
                    sqlColumnInfo.setColumnAlias(columnAlias);
                }
                result.add(sqlColumnInfo.toString());
            }
        });

        return result;
    }

    //后续优化代码SQL生成逻辑在放开
    //public QueryWrapperImpl() {
    //    log.debug("注意：后续请通过（addTable）方法指定主表，否则报错。开放此无参构建器仅仅只是方便BaseServiceImpl的使用");
    //    sqlSB = new StringBuilder("SELECT {} FROM {} {} {} {} {} {} {}");
    //}



    /**
     * 构建SQL == 会直接忽略掉含有默认值得字段，如需构建请手动配置条件
     *
     * @param bean 里面的非空字段，全部用eq条件进行构建
     */
    public QueryWrapperImpl(FirstTable bean) {
        this(bean, false);
    }

    /**
     *
     * @param bean 条件值
     * @param fuzzyQueryFlag true like模糊条件  false等值条件
     */
    public QueryWrapperImpl(FirstTable bean, final boolean fuzzyQueryFlag) {
        this(bean,fuzzyQueryFlag,true);
    }

    /**
     * 构建SQL == 会直接忽略掉含有默认值得字段，如需构建请手动配置条件
     *
     * @param bean           里面的非空字段，全部用eq或like条件进行构建
     * @param fuzzyQueryFlag true like模糊条件  false等值条件
     * @param logicDeleteSupport 查询的时候，如果Bean中有is_del字段，没有显示的设置is_del的值，则自动添加is_del=0
     */
    public QueryWrapperImpl(FirstTable bean, final boolean fuzzyQueryFlag, boolean logicDeleteSupport ) {
        this((Class<FirstTable>) bean.getClass(),logicDeleteSupport);

        //添加SQL片段==eq OR like
        Map<String, Object> beanMap = EnhanceBeanUtil.beanToMap(bean, true, true, true);
        if (CollUtil.isNotEmpty(beanMap)) {
            CollUtil.forEach(beanMap, new CollUtil.KVConsumer<String, Object>() {
                @Override
                public void accept(String key, Object value, int index) {
                    if (fuzzyQueryFlag) {
                        like(key, value);
                    } else {
                        eq(key, value);
                    }
                }
            });
        }
    }

    /**
     *
     * @param clazz 主表
     */
    public QueryWrapperImpl(Class<FirstTable> clazz) {
        this(clazz,true);
    }

    /**
     *
     * @param clazz 主表
     * @param logicDeleteSupport 查询的时候，如果Bean中有is_del字段，没有显示的设置is_del的值，则自动添加is_del=0
     */
    public QueryWrapperImpl(Class<FirstTable> clazz, boolean logicDeleteSupport) {
        Assert.notNull(clazz, "Bean实体反射不能为空，请检查");
        setFirstTableClazz(clazz);
        setLogicDeleteSupport(logicDeleteSupport);
        addTable(clazz, true);
        //中括号顺序内容：select字段、主表、连表的内容、where筛选条件的内容、groupBy分组内容、having的内容、orderby排序内容、分页内容
        sqlSB = new StringBuilder("SELECT {} FROM {} {} {} {} {} {} {}");
    }

    /**
     * 自动连表
     * 必须在主表定义需要被连的表以及字段{@link TableJoin}
     *
     * @param tableClazzs 与主表想连的表
     * @return
     */
    @Override
    public QueryWrapper<FirstTable> autoJoin(final Class... tableClazzs) {
        Assert.notEmpty(tableClazzs);
        addTables(tableClazzs);
        Map<Field, Annotation> joinAnnotationMap = EnhanceReflectUtil.getFieldByAnnotation(getFirstTableClazz(), TableJoin.class);
        if (CollUtil.isNotEmpty(joinAnnotationMap)) {
            //joinOnSqlAppendFlagThreadLocal.set(true);
            CollUtil.forEach(joinAnnotationMap, new CollUtil.KVConsumer<Field, Annotation>() {
                @Override
                public void accept(final Field field, Annotation annotation, int index) {
                    EnhanceAnnotationUtil.emptyToCopyAnnotationFieldValue(annotation, "value", "tableJoinFields");
                    TableJoin tableJoin = (TableJoin) annotation;

                    List<TableJoinField> tableJoinFieldList = Arrays.asList(tableJoin.tableJoinFields());
                    List<TableInnerJoinField> tableInnerJoinFieldList = Arrays.asList(tableJoin.tableInnerJoinFields());
                    List<TableLeftJoinField> tableLeftJoinFieldList = Arrays.asList(tableJoin.tableLeftJoinFields());
                    List<TableRightJoinField> tableRightJoinFieldList = Arrays.asList(tableJoin.tableRightJoinFields());

                    List<? super Annotation> joinAnnotations = new ArrayList<>();
                    joinAnnotations.addAll(tableJoinFieldList);
                    joinAnnotations.addAll(tableInnerJoinFieldList);
                    joinAnnotations.addAll(tableLeftJoinFieldList);
                    joinAnnotations.addAll(tableRightJoinFieldList);

                    //开始连表
                    CollUtil.forEach(joinAnnotations.iterator(), new CollUtil.Consumer() {
                        @Override
                        public void accept(Object value, int index) {
                            Class joinTable = ((Class[]) EnhanceAnnotationUtil.getAnnotationFieldValue((Annotation) value, "joinTable"))[0];
                            if (ArrayUtil.contains(tableClazzs, joinTable)) {
                                joinBuilderByAnnotation(field.getName(), (Annotation) value);
                            }
                        }
                    });

                }
            });
            //joinOnSqlAppendFlagThreadLocal.remove();
        } else {
            //log.info("autoJoin：缺乏连表字段设置");
        }
        return this;
    }

    /**
     * 需要select的字段 == 计算记录数
     * 一旦触发即使select其他字段也会直接忽略，所以此方法仅仅只用来统计行数而已
     *
     * @return
     */
    @Override
    public QueryWrapper<FirstTable> count() {
        selectColumnInfos.clear();
        isCountFlag = true;
        return select(getFirstTableClazz(), SqlFragment.SELECT_COUNT);
    }


    /**
     * 解析列信息 == 方便后续增强
     *
     * @param tableClazz
     * @param selectColumnSqlFragment
     * @return
     */
    private SqlColumnInfo selectColumnInfoAdd(Class tableClazz, String selectColumnSqlFragment) {
        Assert.notBlank(selectColumnSqlFragment, "SQL片段出错：缺乏SELECT的SQL片段(selectColumnSqlFragment)，请检查");
        selectColumnSqlFragment = StrUtil.trim(selectColumnSqlFragment);
        String[] columnInfo = selectColumnSqlFragment.split("\\s+");
        Assert.isTrue(columnInfo.length == 1 || columnInfo.length == 3, "SQL片段出错：({})SELECT的SQL片段要么不起别名，要么起别名需要完整的SQL片段，请检查", selectColumnSqlFragment);
        //列名校验 == 不通过直接抛出异常
        checkColumnNames(tableClazz, columnInfo[0]);
        final TableInfo tableInfo = getTableInfo(tableClazz);
        final String columnName = isColumnNameFlag(columnInfo[0]) ? EnhanceReflectUtil.getDBColumnName(tableClazz, columnInfo[0]) : columnInfo[0];
        String columnAlias = null;
        if (columnInfo.length > 1) {
            Assert.isTrue(StrUtil.equalsAnyIgnoreCase(columnInfo[1], "as"), "SQL片段出错：({})别名关键字出错，请检查", selectColumnSqlFragment);
            columnAlias = columnInfo[2];
            //去单引号
            if (ReUtil.isMatch("'.*'", columnAlias)) {
                columnAlias = ReUtil.get("(?<=').*(?=')", columnAlias, 0);
            }
        }
        final String columnAliasCopy = columnAlias;


        Class columnType = isColumnNameFlag(columnInfo[0]) ? EnhanceReflectUtil.getDBColumnNameClassType(tableClazz, columnName) : null;

        SqlColumnInfo sqlColumnInfo = CollUtil.findOne(selectColumnInfos, new Filter<SqlColumnInfo>() {
            @Override
            public boolean accept(SqlColumnInfo selectColumnInfo) {
                if(StrUtil.isNotBlank(columnAliasCopy)) {
                    return selectColumnInfo.getTableInfo() == tableInfo && StrUtil.equals(selectColumnInfo.getColumnName(), columnName) && StrUtil.equals(selectColumnInfo.getColumnAlias(), columnAliasCopy);
                }else {
                    return selectColumnInfo.getTableInfo() == tableInfo && StrUtil.equals(selectColumnInfo.getColumnName(), columnName);
                }

            }
        });

        sqlColumnInfo = sqlColumnInfo != null ? sqlColumnInfo : new SqlColumnInfo(tableInfo, columnName, columnType, columnAlias,SqlColumnInfo.SQL_TYPE_SELECT);
        sqlColumnInfo.setRowSqlFragment(selectColumnSqlFragment);
        selectColumnInfos.add(sqlColumnInfo);
        //返回容器里面的对象
        return sqlColumnInfo;
    }

    /**
     * 批量解析列信息 == 方便后续增强
     *
     * @param tableClazz
     * @param selectColumnSqlFragments
     * @return
     */
    private Set<SqlColumnInfo> selectColumnInfoAdds(Class tableClazz, String... selectColumnSqlFragments) {
        selectColumnSqlFragments = ArrayUtil.removeBlank(selectColumnSqlFragments);
        if (ObjectUtil.isAllNotEmpty(tableClazz, selectColumnSqlFragments)) {
            for (String selectColumnSqlFragment : selectColumnSqlFragments) {
                selectColumnInfoAdd(tableClazz, selectColumnSqlFragment);
            }
        }
        return selectColumnInfos;
    }

    /**
     * 需要select的字段 == 字段结果集是key
     * <p>
     * 其实内部调的依然还是{@link QueryWrapperImpl#select(Class, String...)}
     *
     * @param dbFieldName      主表的列名
     * @param dbFieldNameAlias 主表的{dbFieldName}列的别名 == 用于结果集
     * @return
     */
    @Override
    public QueryWrapper<FirstTable> selectAlias(String dbFieldName, String dbFieldNameAlias) {
        return selectAlias(getFirstTableClazz(), dbFieldName, dbFieldNameAlias);
    }


    /**
     * 需要select的字段 == 字段结果集是key
     * <p>
     * 其实内部调的依然还是{@link QueryWrapperImpl#select(Class, String...)}
     *
     * @param tableClazz       列名{dbFieldName}所属的表
     * @param dbFieldName      表{tableClazz}的列名
     * @param dbFieldNameAlias 表{tableClazz}的{dbFieldName}列的别名 == 用于结果集
     * @return
     */
    @Override
    public QueryWrapper<FirstTable> selectAlias(Class tableClazz, String dbFieldName, String dbFieldNameAlias) {
        Assert.isFalse(StrUtil.hasBlank(dbFieldName, dbFieldNameAlias), "SQL片段异常：【{}、{}】调用selectAlias方法列名、列别名都不能为空，请检查");
        return select(tableClazz, StrUtil.format("{} AS {}", dbFieldName, dbFieldNameAlias));
    }


    /**
     * 需要select的字段
     * <p>
     * select("字段名") ==》 主表的字段名的别名即本身  => 表别名.字段名
     * select(主表, "字段名a") select(副表, "字段名a") ==》 非主表的字段全部需要起别名格式 => 表别名.字段名 AS '表别名.字段名'
     * select("字段名 AS 字段名别名")  => 表别名.字段名 AS '字段名别名'
     * select("字段名 AS '字段名别名'")  => 表别名.字段名 AS '字段名别名'
     *
     * @param dbFieldNames 字段名 == 默认全是主表的字段
     *                     如    name => 表chaiji_status => cs_8d053.account
     * @return
     */
    @Override
    public QueryWrapper<FirstTable> select(String... dbFieldNames) {
        return select(getFirstTableClazz(), dbFieldNames);
    }

    /**
     * 需要select的字段 == 支持表别名SQL
     *
     * select("字段名") ==》 主表的字段名的别名即本身  => 表别名.字段名
     * select(tableClazz,"*") ==》 表{tableClazz}的*
     * select("*") ==》 主表的*
     * select(主表, "字段名a") select(副表, "字段名a") ==》 非主表的字段全部需要起别名格式 => 表别名.字段名 AS '表别名.字段名'
     * select("字段名 AS 字段名别名")  => 表别名.字段名 AS '字段名别名'
     * select("字段名 AS '字段名别名'")  => 表别名.字段名 AS '字段名别名'
     *
     * @param tableClazz   字段所属的表 == 内部会自动为每个字段名加表别名
     * @param dbFieldNames 字段名
     *                     如    name => 表chaiji_status => cs_8d053.account
     * @return
     */
    @Override
    public QueryWrapper<FirstTable> select(Class tableClazz, String... dbFieldNames) {
        tableClazz = tableClazz == null ? firstTableClazz : tableClazz;
        addTable(tableClazz, false);
        dbFieldNames = ArrayUtil.removeBlank(dbFieldNames);
        StrUtil.trim(dbFieldNames); //去头尾空白
        Assert.notEmpty(dbFieldNames, "缺乏select的字段，请检查");
        if (ArrayUtil.isNotEmpty(dbFieldNames)) {
            for (int i = 0; i < dbFieldNames.length; i++) {
                //列名前面加上表别名
                selectColumnInfoAdds(tableClazz, dbFieldNames);
            }
        }
        return this;
    }

    /**
     * 校验连表的注解字段值
     * {@link TableJoinField,TableLeftJoinField,TableRightJoinField,TableInnerJoinField}
     *
     * @param annotation
     * @param <T>
     * @return
     */
    private <T extends Annotation> T checkAnnotation(T annotation) {
        Assert.notNull(annotation, "缺乏注解");
        if (annotation.getClass() == TableJoinField.class) {
            EnhanceAssert.annotationFieldValueNotEmpty(annotation, "joinSymbol", "注解（@{}）缺乏连表SQL片段（joinSymbol），请检查", annotation.annotationType().getSimpleName());
        }
        EnhanceAssert.annotationFieldValueNotEmpty(annotation, "joinTable", "注解（@{}）缺乏所连的表（joinTable），请检查", annotation.annotationType().getSimpleName());
        EnhanceAssert.annotationFieldValueNotEmpty(annotation, "joinFieldName", "注解（@{}）缺乏所连的表字段（joinFieldName），请检查", annotation.annotationType().getSimpleName());
        return annotation;
    }

    private QueryWrapper<FirstTable> joinBuilderByAnnotation(String masterFieldName, Annotation joinFieldAnnotation) {
        checkAnnotation(joinFieldAnnotation);
        //连表类型
        String joinSqlFragment = null;
        //主表所需连的表
        Class joinTable = ((Class[]) EnhanceAnnotationUtil.getAnnotationFieldValue(joinFieldAnnotation, "joinTable"))[0];
        //连表的字段
        String joinFieldName = (String) EnhanceAnnotationUtil.getAnnotationFieldValue(joinFieldAnnotation, "joinFieldName");
        if (ObjectUtil.equal(joinFieldAnnotation.annotationType(), TableJoinField.class)) {
            joinSqlFragment = (String) EnhanceAnnotationUtil.getAnnotationFieldValue(joinFieldAnnotation, "joinSymbol");
            Assert.notBlank(joinSqlFragment, "【{}】注解缺乏连表字段（joinSymbol），请检查", joinFieldAnnotation.annotationType().getName());
        }


        if (ObjectUtil.equal(joinFieldAnnotation.annotationType(), TableLeftJoinField.class)) {
            joinSqlFragment = SqlFragment.LEFT_JOIN;
        } else if (ObjectUtil.equal(joinFieldAnnotation.annotationType(), TableInnerJoinField.class)) {
            joinSqlFragment = SqlFragment.INNER_JOIN;
        } else if (ObjectUtil.equal(joinFieldAnnotation.annotationType(), TableRightJoinField.class)) {
            joinSqlFragment = SqlFragment.RIGHT_JOIN;
        }

        join(getFirstTableClazz(), joinTable, joinSqlFragment, true)
                .on(masterFieldName, joinFieldName);


        return this;
    }

    /**
     * 左连
     * 【FirstTable】主表左连【secondTableClazz】表
     *
     * @param secondTableClazz 此表与主表进行左连
     * @return
     */
    @Override
    public <SecondTable> OnWrapperImpl<FirstTable, FirstTable, SecondTable> leftJoin(Class<SecondTable> secondTableClazz) {
        return leftJoin(secondTableClazz, false);
    }

    /**
     * 左连
     * 【FirstTable】主表左连【secondTableClazz】表
     *
     * @param secondTableClazz 此表与主表进行左连
     * @param joinOnSqlAppendFlag 是否将on后面的判断信息连到已有的join上
     * @return
     */
    @Override
    public <SecondTable> OnWrapperImpl<FirstTable, FirstTable, SecondTable> leftJoin(Class<SecondTable> secondTableClazz, boolean joinOnSqlAppendFlag) {
        return leftJoin(getFirstTableClazz(), secondTableClazz, joinOnSqlAppendFlag);
    }

    /**
     * 左连
     * 【leftableClazz】表左连【secondTableClazz】表
     *
     * @param leftableClazz 左表
     * @param secondTableClazz 右表
     * @return
     */
    @Override
    public <T, SecondTable> OnWrapperImpl<FirstTable, T, SecondTable> leftJoin(Class<T> leftableClazz, Class<SecondTable> secondTableClazz) {
        return leftJoin(leftableClazz, secondTableClazz, false);
    }

    /**
     * 左连
     * 【leftableClazz】表左连【secondTableClazz】表
     * <p>
     * 注意：必须确保此表【leftableClazz】在前面拼好的SQL能找到，否则报错
     *
     * @param leftableClazz 左表
     * @param secondTableClazz 右表
     * @param joinOnSqlAppendFlag 是否将on后面的判断信息连到已有的join上
     * @return
     */
    @Override
    public <T, SecondTable> OnWrapperImpl<FirstTable, T, SecondTable> leftJoin(Class<T> leftableClazz, Class<SecondTable> secondTableClazz, boolean joinOnSqlAppendFlag) {
        //查看在前面SQL是否能找到左表，否则报错
        TableInfo tableInfo = getTableInfo(leftableClazz);
        return join(leftableClazz, secondTableClazz, SqlFragment.LEFT_JOIN, joinOnSqlAppendFlag);
    }

    /**
     * 等值连
     * 【FirstTable】主表等值连【secondTableClazz】表
     *
     * @param secondTableClazz 此表与主表进行左连
     * @return
     */
    @Override
    public <SecondTable> OnWrapperImpl<FirstTable, FirstTable, SecondTable> innerJoin(Class<SecondTable> secondTableClazz) {
        return innerJoin(secondTableClazz, false);
    }

    /**
     * 等值连
     * 【FirstTable】主表等值连【secondTableClazz】表
     *
     * @param secondTableClazz 此表与主表进行左连
     * @param joinOnSqlAppendFlag 是否将on后面的判断信息连到已有的join上
     * @return
     */
    @Override
    public <SecondTable> OnWrapperImpl<FirstTable, FirstTable, SecondTable> innerJoin(Class<SecondTable> secondTableClazz, boolean joinOnSqlAppendFlag) {
        return innerJoin(getFirstTableClazz(), secondTableClazz, joinOnSqlAppendFlag);
    }

    /**
     * 等值连
     * @param leftableClazz 左表
     * @param secondTableClazz 右表
     * @return
     */
    @Override
    public <T, SecondTable> OnWrapperImpl<FirstTable, T, SecondTable> innerJoin(Class<T> leftableClazz, Class<SecondTable> secondTableClazz) {
        return innerJoin(leftableClazz, secondTableClazz, false);
    }

    /**
     * 等值连
     * 【leftableClazz】表等值连【secondTableClazz】表
     * <p>
     * 注意：必须确保此表【leftableClazz】在前面拼好的SQL能找到，否则报错
     *
     * @param leftableClazz 左表
     * @param secondTableClazz 右表
     * @param joinOnSqlAppendFlag 是否将on后面的判断信息连到已有的join上
     * @return
     */
    @Override
    public <T, SecondTable> OnWrapperImpl<FirstTable, T, SecondTable> innerJoin(Class<T> leftableClazz, Class<SecondTable> secondTableClazz, boolean joinOnSqlAppendFlag) {
        //查看在前面SQL是否能找到左表，否则报错
        TableInfo tableInfo = getTableInfo(leftableClazz);
        return join(leftableClazz, secondTableClazz, SqlFragment.INNER_JOIN, joinOnSqlAppendFlag);
    }

    /**
     * 右连
     * 【FirstTable】主表右连【secondTableClazz】表
     *
     * @param secondTableClazz 此表与主表进行右连
     * @return
     */
    @Override
    public <SecondTable> OnWrapperImpl<FirstTable, FirstTable, SecondTable> rightJoin(Class<SecondTable> secondTableClazz) {
        return rightJoin(secondTableClazz, false);
    }

    /**
     * 右连
     * 【FirstTable】主表右连【secondTableClazz】表
     *
     * @param secondTableClazz 此表与主表进行右连
     * @param joinOnSqlAppendFlag 是否将on后面的判断信息连到已有的join上
     * @return
     */
    @Override
    public <SecondTable> OnWrapperImpl<FirstTable, FirstTable, SecondTable> rightJoin(Class<SecondTable> secondTableClazz, boolean joinOnSqlAppendFlag) {
        return rightJoin(getFirstTableClazz(), secondTableClazz, joinOnSqlAppendFlag);
    }

    /**
     * 右连
     * @param leftableClazz 左表
     * @param secondTableClazz 右表
     * @return
     */
    @Override
    public <T, SecondTable> OnWrapperImpl<FirstTable, T, SecondTable> rightJoin(Class<T> leftableClazz, Class<SecondTable> secondTableClazz) {
        return rightJoin(leftableClazz, secondTableClazz, false);
    }

    /**
     * 右连
     * 【leftableClazz】表右连【secondTableClazz】表
     * 注意：必须确保此表【leftableClazz】在前面拼好的SQL能找到，否则报错
     *
     * @param leftableClazz 左表
     * @param secondTableClazz 右表
     * @param joinOnSqlAppendFlag 是否将on后面的判断信息连到已有的join上
     * @return
     */
    @Override
    public <T, SecondTable> OnWrapperImpl<FirstTable, T, SecondTable> rightJoin(Class<T> leftableClazz, Class<SecondTable> secondTableClazz, boolean joinOnSqlAppendFlag) {
        //查看在前面SQL是否能找到左表，否则报错
        TableInfo tableInfo = getTableInfo(leftableClazz);
        return join(leftableClazz, secondTableClazz, SqlFragment.RIGHT_JOIN, joinOnSqlAppendFlag);
    }


    private <T, SecondTable> OnWrapperImpl<FirstTable, T, SecondTable> join(Class<T> leftableClazz, Class<SecondTable> secondTableClazz, String joinFragment) {
        return join(leftableClazz, secondTableClazz, joinFragment, false);
    }

    /**
     * 连表SQL构成
     *
     * @param secondTableClazz 需要连的表
     * @param joinFragment     连表SQL关键字 参考{@link SqlFragment#LEFT_JOIN}
     * @return
     */
    private <T, SecondTable> OnWrapperImpl<FirstTable, T, SecondTable> join(Class<T> leftableClazz, Class<SecondTable> secondTableClazz, String joinFragment, boolean joinOnSqlAppendFlag) {
        Assert.notNull(secondTableClazz);
        joinFragment = StrUtil.blankToDefault(joinFragment, SqlFragment.INNER_JOIN);
        addTable(secondTableClazz, false);
        TableInfo secondTableInfo = getTableInfo(secondTableClazz);


        if (joinOnSqlAppendFlag && joinSqlFragmentsIndexOf(joinFragment, secondTableClazz) == -1) {
            joinSqlFragments.add(StrUtil.format(" {} {} AS {} ", joinFragment, secondTableInfo.getTableName(), secondTableInfo.getTableAlias()));
        } else if (joinOnSqlAppendFlag == false) {
            joinSqlFragments.add(StrUtil.format(" {} {} AS {} ", joinFragment, secondTableInfo.getTableName(), secondTableInfo.getTableAlias()));
        }


        return new OnWrapperImpl(getFirstTableClazz(), leftableClazz, secondTableClazz, this, joinOnSqlAppendFlag, joinFragment);
    }

    /**
     * 分组
     *
     * @param groupFieldNames 属于主表的字段
     * @return
     */
    @Override
    public QueryWrapper<FirstTable> groupBy(String... groupFieldNames) {
        return groupBy(getFirstTableClazz(), groupFieldNames);
    }

    /**
     * 分组
     *
     * @param tableClazz      {groupFieldNames}所属的表字段
     * @param groupFieldNames
     * @return
     */
    @Override
    public QueryWrapper<FirstTable> groupBy(Class tableClazz, String... groupFieldNames) {
        groupFieldNames = ArrayUtil.removeBlank(groupFieldNames);
        for (String groupFieldName : groupFieldNames) {
            groupByFieldSqlFragments.add(getColumnAddTableAlias(tableClazz, groupFieldName));
        }
        return this;
    }

    /**
     * 有having筛选条件则使用这个
     *
     * @param groupFieldNames 待分组的主表字段
     * @return
     */
    @Override
    public HavingWrapperImpl<FirstTable, FirstTable, QueryWrapper<FirstTable>> groupByHaving(String... groupFieldNames) {
        groupBy(groupFieldNames);
        return groupByHaving(getFirstTableClazz(), groupFieldNames);
    }

    /**
     * 有having筛选条件则使用这个
     *
     * @param groupFieldNames 待分组的字段
     * @return
     */
    @Override
    public <SecondTable> HavingWrapperImpl<FirstTable, SecondTable, QueryWrapper<FirstTable>> groupByHaving(Class<SecondTable> tableClazz, String... groupFieldNames) {
        groupBy(tableClazz, groupFieldNames);
        return new HavingWrapperImpl(this, getFirstTableClazz(), tableClazz);
    }

    /**
     * 升序
     *
     * @param orderByFieldNames 待分组的主表字段
     * @return
     */
    @Override
    public QueryWrapper<FirstTable> asc(String... orderByFieldNames) {
        return asc(getFirstTableClazz(), orderByFieldNames);
    }

    /**
     * 升序
     *
     * @param tableClazz        {orderByFieldNames}字段所属表
     * @param orderByFieldNames
     * @return
     */
    @Override
    public QueryWrapper<FirstTable> asc(Class tableClazz, String... orderByFieldNames) {
        return orderByBuild(tableClazz, SqlFragment.ASC, orderByFieldNames);
    }

    /**
     * 倒序
     *
     * @param orderByFieldNames 待分组的主表字段
     * @return
     */
    @Override
    public QueryWrapper<FirstTable> desc(String... orderByFieldNames) {
        return desc(getFirstTableClazz(), orderByFieldNames);
    }

    /**
     * 倒序
     *
     * @param tableClazz        {orderByFieldNames}字段所属表
     * @param orderByFieldNames
     * @return
     */
    @Override
    public QueryWrapper<FirstTable> desc(Class tableClazz, String... orderByFieldNames) {
        return orderByBuild(tableClazz, SqlFragment.DESC, orderByFieldNames);
    }


    /**
     * asc、desc升序倒序构建器
     *
     * @param tableClazz        {orderByFieldNames}字段所属表
     * @param symbol            升序、倒序关键字 {@link SqlFragment#DESC}
     * @param orderByFieldNames
     * @return
     */
    private QueryWrapper<FirstTable> orderByBuild(Class tableClazz, String symbol, String... orderByFieldNames) {
        orderByFieldNames = ArrayUtil.removeBlank(orderByFieldNames);
        if (ArrayUtil.isNotEmpty(orderByFieldNames)) {
            for (String orderByFieldName : orderByFieldNames) {
                orderBySqlFragments.add(StrUtil.join(StrUtil.SPACE, getColumnAddTableAlias(tableClazz, orderByFieldName), symbol));
            }
        }
        return this;
    }

    /**
     * 取消分页
     *
     * @return
     */
    @Override
    public QueryWrapper<FirstTable> notPage() {
        return page(-1, -1);
    }

    /**
     * 分页
     *
     * @param pageNum 第N页 从1开始
     * @param pageNum 每页显示的条数
     * @return
     */
    @Override
    public QueryWrapper<FirstTable> page(long pageNum, long pageSize) {
        if (pageNum > 0 && pageSize > 0) { //分页
            return limit((pageNum - 1) * pageSize, pageSize);
        } else { //不分页
            return limit(-1, -1);
        }
    }

    /**
     * 分页
     *
     * @param startIndex 从第N（启始从0开始）条记录开始查找（包括第N条）
     * @param pageSize   每页最多的记录数
     * @return
     */
    @Override
    public QueryWrapper<FirstTable> limit(long startIndex, long pageSize) {
        this.limitStartIndex = startIndex;
        this.limitPageSize = pageSize;
        this.pageSize = limitPageSize;
        if (startIndex < 0 || pageSize < 0) {
            this.pageNum = -1L;
        } else {
            this.pageNum = startIndex / pageSize + 1;
        }
        return this;
    }


    @Override
    public String toString() {
        return getSQL();
    }

    /**
     * 获取完整的SQL语句
     *
     * @return
     */
    @Override
    public String getSQL() {
        Assert.notNull(getMasterTableInfo(),"异常：系统缺乏主表，请检查（不可通过泛型定义主表，请通过有入参的构造函数构建或者使用addTable显示指定主表）, 请检查");

        //处理SQL语句 == 添加逻辑删除字段
        postSQLDeal();

        TableInfo firstTableInfo = getTableInfo(getFirstTableClazz());

        //select字段
        String selectSqL = SqlFragment.ASTERISK;
        if (CollUtil.isNotEmpty(selectColumnInfos)) {
            //不同表但同名列处理
            List selectColumnNamesFormatter = selectColumnNamesFormatter();
            selectSqL = CollUtil.join(selectColumnNamesFormatter, StrUtil.COMMA);

            //selectSqL = CollUtil.join(selectColumnNames, StrUtil.COMMA);
        }

        //主表的内容
        String firstTableSql = StrUtil.format(" {} AS {} ", firstTableInfo.getTableName(), firstTableInfo.getTableAlias());

        //join片段
        String joinSql = StrUtil.EMPTY;
        if (CollUtil.isNotEmpty(joinSqlFragments)) {
            joinSql = CollUtil.join(joinSqlFragments, StrUtil.SPACE);
        }

        //条件
        String whereSql = StrUtil.EMPTY;
        if (CollUtil.isNotEmpty(getWhereSqlFragments())) {
            whereSql = " WHERE {} ";
            whereSql = StrUtil.format(whereSql, CollUtil.join(getWhereSqlFragments(), SqlFragment.WHERE_AND));
        }

        //groupby的内容
        String groupbySql = StrUtil.EMPTY;
        if (CollUtil.isNotEmpty(groupByFieldSqlFragments)) {
            groupbySql = " GROUP BY {} ";
            groupbySql = StrUtil.format(groupbySql, CollUtil.join(groupByFieldSqlFragments, StrUtil.COMMA));
        }

        //having的内容
        String havingSql = StrUtil.EMPTY;
        if (CollUtil.isNotEmpty(havingWhereSqlFragments)) {
            havingSql = " HAVING {} ";
            havingSql = StrUtil.format(havingSql, CollUtil.join(getHavingWhereSqlFragments(), SqlFragment.WHERE_AND));
        }

        //orderby的内容
        String orderbySql = " ORDER BY {}";
        if (CollUtil.isEmpty(orderBySqlFragments)) {
            desc("id");
        }
        orderbySql = StrUtil.format(orderbySql, CollUtil.join(orderBySqlFragments, StrUtil.COMMA));

        //limit的内容
        String limitSql = StrUtil.EMPTY;
        if (ObjectUtil.isAllNotEmpty(limitStartIndex, limitPageSize) && limitStartIndex >= 0 && limitPageSize > 0) {
            limitSql = " LIMIT ?,? ";
        }

        return StrUtil.format(sqlSB, selectSqL, firstTableSql, joinSql, whereSql, groupbySql, havingSql, orderbySql, limitSql);
    }

}
